package com.example.listener;

import com.example.constant.SysJobConstant;
import com.example.po.SysJob;
import com.example.service.SysJobHandlerProxyService;
import com.example.service.SysJobService;
import com.example.thread.DynamicThreadPool;
import com.example.utils.DateTimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author chentao
 * @version 1.0
 * @description: 实现任务的分配和故障转移
 * @date 2025/8/28 23:31
 */
@Service
public class SysJobHandlerListener implements InitializingBean, DisposableBean, ApplicationListener<ContextRefreshedEvent> {

    Logger logger = LoggerFactory.getLogger(getClass());

    @Value("${sys.server.poll.period}")
    private int pollPeriod;

    @Value("${sys.server.poll.max-size}")
    private int maxCount;

    @Value("${sys.server.retry-period}")
    private int retryPeriod;

    @Resource
    private SysJobService sysJobService;

    @Resource
    private SysJobHandlerProxyService sysJobHandlerProxyService;

    private ScheduledExecutorService scheduledExecutorService;

    private DynamicThreadPool dynamicThreadPool;

    @Override
    public void afterPropertiesSet() throws Exception {
        this.dynamicThreadPool = new DynamicThreadPool();
        this.scheduledExecutorService = Executors.newScheduledThreadPool(10);
    }

    @Override
    public void destroy() throws Exception {
        if (this.scheduledExecutorService != null) {
            this.scheduledExecutorService.shutdownNow();
        }
        this.dynamicThreadPool.stop();
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (event.getApplicationContext().getParent() == null) {
            this.dynamicThreadPool.start();
            this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<SysJob> jobList = sysJobService.getRunningJobList(maxCount);
                        if (!jobList.isEmpty()) {
                            CountDownLatch countDownLatch = new CountDownLatch(jobList.size());
                            for (SysJob sysJob : jobList) {
                                dynamicThreadPool.execute(new WorkHandlerThread(sysJob, countDownLatch));
                            }
                            countDownLatch.await();
                        }
                    } catch (Exception e) {
                        logger.warn("sysJobHandler run job error : " + e.getMessage());
                    }
                }
            }, 0, pollPeriod, TimeUnit.SECONDS);
        }
    }

    class WorkHandlerThread implements Runnable {

        private SysJob sysJob;

        private CountDownLatch countDownLatch;

        public WorkHandlerThread(SysJob sysJob, CountDownLatch countDownLatch) {
            this.sysJob = sysJob;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            int retryCount = sysJob.getRetryCount();
            try {
                sysJob.setStatus(SysJobConstant.SUCCESS);
                sysJobHandlerProxyService.handler(sysJob);
            } catch (Exception e) {
                sysJob.setStatus(SysJobConstant.ERROR);
                sysJob.setRetryCount(retryCount + 1);
                sysJob.setRetryTime(DateTimeUtils.addSec(retryPeriod * retryCount));
            } finally {
                sysJob.recalculateFields();
                sysJobService.saveOrUpdate(sysJob);
                countDownLatch.countDown();
            }
        }
    }
}
